/**
 * 数据库管理模型
 * 
 * @author Ho Lee
 * @since 2021/7/12
 * @version 0.0.3
 */

package xyz.holee.db.KeyValueDatabase;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

public class Database {
    private ArrayList<KeyValue> list;
    private File file = null;

    public Database(String fileName) {
        this.file = new File(fileName);
        list = new ArrayList<>();
        try {
            this.file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
    public void setNewDatabase(String fileName) {
        this.file = new File(fileName);
        try {
            this.file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
    public boolean isFile() {
        return this.file.isFile();
    }
    public boolean canRead() {
        return this.file.canRead();
    }
    public boolean canWrite() {
        return this.file.canWrite();
    }
    public ArrayList<KeyValue> load(String fileName) {
        this.file = new File(fileName);
        return this.load();
    }
    public ArrayList<KeyValue> load() {
        this.list.clear();
        if (this.isFile() && this.canRead()) {
            String line = "";
            KeyValue keyvalue = null;
            int kvIndex = -1;
            try {
                BufferedReader reader = new BufferedReader(new FileReader(this.file));
                line = reader.readLine();
                while (line != null) {
                    char c = line.charAt(0);
                    if (c == '-') {
                        keyvalue = new KeyValue(line.substring(1));
                        this.list.add(keyvalue);
                        ++kvIndex;
                    } else if (c == ':') {
                        String l = line.substring(1);
                        keyvalue.setKeyValue(l);
                        this.list.set(kvIndex, keyvalue);
                    }
                    line = reader.readLine();
                }
                reader.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return null;
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }
        return this.list;
    }
    public boolean save(String fileName, String text) {
        try {
            BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
            writer.write(text);
            writer.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
    public boolean save() {
        StringBuilder fileContent = new StringBuilder();
        for (KeyValue item : this.list) {
            fileContent.append("-" + item.className + "\r\n");
            for (int i = 0; i < item.key.size(); ++i) {
                fileContent.append(":" + item.key.get(i) + "=" + item.value.get(i) + "\r\n");
            }
        }
        return this.save(this.file.getPath(), fileContent.toString());
    }
    public boolean add(String path, String value) {
        String []p = path.split("\\.", 2);
        if (p.length != 2) {
            return false;
        } else {
            String className = p[0];
            String key = p[1];
            boolean hasClass = false;
            for (KeyValue item : list) {
                if (item.className.equals(className)) {
                    hasClass = true;
                    for (int i = 0; i < item.key.size(); ++i) {
                        if (key.equals(item.key.get(i))) {
                            return false;
                        }
                    }
                    item.key.add(key);
                    item.value.add(value);
                }
            }
            if (!hasClass) {
                KeyValue kv = new KeyValue(className);
                ArrayList<String> newKey = new ArrayList<>();
                ArrayList<String> newValue = new ArrayList<>();
                newKey.add(key);
                newValue.add(value);
                kv.key = newKey;
                kv.value = newValue;
                this.list.add(kv);
            }
            return this.save();
        }
    }
    public boolean update(String path, String value) {
        String []p = path.split("\\.", 2);
        if (p.length != 2) {
            return false;
        } else {
            String className = p[0];
            String key = p[1];
            boolean hasClass = false;
            boolean hasKey = false;
            for (KeyValue item : this.list) {
                if (item.className.equals(className)) {
                    hasClass = true;
                    for (int i = 0; i < item.key.size(); ++i) {
                        if (key.equals(item.key.get(i))) {
                            hasKey = true;
                            item.value.set(i, value);
                        }
                    }
                }
            }
            if (hasClass && hasKey) {
                return this.save();
            } else {
                return false;
            }
        }
    }
    public int getInt(String path, int default_value) {
        String resString = this.getString(path, "");
        if (resString.equals("")) {
            return default_value;
        }
        try {
            int res = Integer.parseInt(resString);
            return res;
        } catch (NumberFormatException e) {
            return default_value;
        }
    }
    public float getFloat(String path, float default_value) {
        String resString = this.getString(path, "");
        if (resString.equals("")) {
            return default_value;
        }
        try {
            float res = Float.parseFloat(resString);
            return res;
        } catch (NumberFormatException e) {
            return default_value;
        }
    }
    public String getString(String path, String default_value) {
        String []p = path.split("\\.", 2);
        if (p.length != 2) {
            return default_value;
        } else {
            String className = p[0];
            String key = p[1];
            for (KeyValue item : this.list) {
                if (item.className.equals(className)) {
                    for (int i = 0; i < item.key.size(); ++i) {
                        if (key.equals(item.key.get(i))) {
                            return item.value.get(i);
                        }
                    }
                }
            }
            return default_value;
        }
    }
    public ArrayList<String> getHeadString(String _class, String head) {
        ArrayList<String> result = new ArrayList<>();
        String className = _class;
        String key = head;
        for (KeyValue item : this.list) {
            if (item.className.equals(className)) {
                for (int i = 0; i < item.key.size(); ++i) {
                    if (key.equals(item.key.get(i).substring(0, key.length()))) {
                        result.add(item.value.get(i));
                    }
                }
                break;
            }
        }
        return result;
    }
    public boolean remove(String path) {
        String []p = path.split("\\.", 2);
        int index = 0;
        if (p.length == 1) {
            for (KeyValue item : this.list) {
                if (item.className.equals(path)) {
                    this.list.remove(index);
                }
                ++index;
            }
        } else if (p.length == 2) {
            String _class = p[0];
            String key = p[1];
            boolean needBreak = false;
            for (KeyValue item : this.list) {
                if (item.className.equals(_class)) {
                    for (String _key : item.key) {
                        if (_key.equals(key)) {
                            item.key.remove(index);
                            item.value.remove(index);
                            needBreak = true;
                            break;
                        }
                        ++index;
                    }
                    if (needBreak) {
                        break;
                    }
                }
            }
        }
        return this.save();
    }
}
